#include "stdafx.h"
#include "TimerTestSuite.h"
#include "TestActiveObject.h"

namespace CO { namespace SpiFoundation { namespace RoomFW {

const SpiFoundation::RoomFW::ActiveObject::PortPriority TimerTestAO::PortPriorities[] = {
	PRI_NORMAL	// PORT_TIMER
};
const size_t TimerTestAO::PortPrioritiesSize = sizeof(TimerTestAO::PortPriorities) / sizeof(IActiveObject::PortPriority);
BOOST_STATIC_ASSERT(TimerTestAO::PortPrioritiesSize == TimerTestAO::PORT_COUNT + 1);

void TimerTestAO::setTestCase(TestCase testCase)
{
	testCase_ = testCase;
}

TimerTestAO::TimerTestAO(void) :
	//
	// Initialize Base class.
	//
	ActiveObject(PORT_COUNT,
				 SpiFoundation::RoomFW::ActiveObject::PortPriorityArray(&PortPriorities[0], &PortPriorities[PortPrioritiesSize])),
	//
	// Initialize attributes.
	//
	testErrorMessage(),
	testCase_(None),
	timerId_(RoomTimer::InvalidTimerId),
	timeout_(0),
	count_(0),
	startTime_(),
	timerResolution_(0),
	timeoutCount_(0)
{
	::ZeroMemory(&startTime_, sizeof(startTime_));
}

TimerTestAO::~TimerTestAO(void)
{
}

const char* TimerTestAO::getPortName(size_t portIndex) const
{
	//
	// Text for each port name for debugging.
	//
	const char* retVal = "***UNKNOWN***";
	switch (portIndex)
	{
	case PORT_TIMER:
		retVal = "PORT_TIMER";
		break;
	default:
		ASSERT(false); // Invalid port.
	};
	return retVal;
}

SpiFoundation::RoomFW::IContractCheckerSPC
TimerTestAO::getContractChecker(SpiFoundation::RoomFW::PortIndex portIndex) const
{
	//
	// Return the contract checker for each port.
	//
	SpiFoundation::RoomFW::IContractCheckerSPC retVal = SpiFoundation::RoomFW::IContractCheckerSPC();
	switch (portIndex.port())
	{
	default:
		ASSERT(false); // Invalid port.
	}
	return retVal;
}

SpiFoundation::RoomFW::ObjectId TimerTestAO::getObjectId(void) const
{
	return 0;
}

const char* TimerTestAO::getObjectName(void) const
{
	//
	// Add text for object name for debugging.
	//
	return "TimerTestAO";
}

void TimerTestAO::onStart(void)
{
	// Start Test.
	testErrorMessage.clear();
	count_ = 0;
	timeoutCount_ = 0;
	if (None != testCase_)
	{
		// Get timer resolution.
		if ((OneShot == testCase_) || (MultiOneShot == testCase_))
		{
			timeout_ = 1;
			TimeValue tv;
			tv.setMilliseconds(timeout_);
			SimpleWaitableTimer timer;
			VERIFY(timer.create());
			int diff = 0;
			for (int i = 0; i < 10; ++i)
			{
				::_ftime(&startTime_);
				VERIFY(timer.set(tv));
				VERIFY(timer.wait(50));
				diff += this->diffTime();
			}
			timerResolution_ = ((diff / 10 + 1) - static_cast<int>(timeout_)) * 2; // Win not RT, so double average timeout for error.
		}
		else
		{
			timeout_ = 1;
			TimeValue tv;
			tv.setMilliseconds(timeout_);
			SimpleWaitableTimer timer;
			VERIFY(timer.create());
			VERIFY(timer.set(0, tv));
			VERIFY(timer.wait(50));
			::_ftime(&startTime_);
			for (int i = 0; i < 10; ++i)
			{
				VERIFY(timer.wait(50));
			}
			int diff = this->diffTime();
			timerResolution_ = ((diff / 10 + 1) - static_cast<int>(timeout_)) * 2; // Win not RT, so double average timeout for error.
		}
		G_LOG_1(LV_SOFTWARE, "Timer Resolution = %u", timerResolution_);

		if (MultiOneShot == testCase_)
		{
			timeout_ = 20;
			::_ftime(&startTime_);
			TimeValue tv;
			for (int i = 0; i < MultiOneShotTimerCount; ++i)
			{
				tv.setMilliseconds(timeout_);
				timerId_ = this->setTimer(tv);
				if (RoomTimer::InvalidTimerId == timerId_)
				{
					std::stringstream errMsg;
					errMsg << "Set timer failed with timeout of " << timeout_ << " ms";
					testErrorMessage = errMsg.str();
					this->signalStop();
					break;
				}
				else
				{
					timeout_ += 20;
					timerIdList_.push_back(timerId_);
				}
			}
#if ACTIVE_OBJECT_TIMER_THREADS
			tv.setMilliseconds(30000);
			for (int i = 0; i < MultiOneShotTimerCount; ++i)
			{
				timerId_ = this->setTimer(tv);
				if (RoomTimer::InvalidTimerId == timerId_)
				{
					std::stringstream errMsg;
					errMsg << "Set timer failed with timeout of " << timeout_ << " ms";
					testErrorMessage = errMsg.str();
					this->signalStop();
					break;
				}
				else
				{
					timerIdList_.push_back(timerId_);
				}
			}
#endif
			timeout_ = 20;
			timerId_ = timerIdList_.front();
		}
		else
		{
			timeout_ = 1;
			::_ftime(&startTime_);
			TimeValue tv;
			tv.setMilliseconds(timeout_);
			timerId_ = this->setTimer(tv, ((OneShot == testCase_) ? 0 : tv));
			if (RoomTimer::InvalidTimerId == timerId_)
			{
				std::stringstream errMsg;
				errMsg << "Set timer failed with timeout of " << timeout_ << " ms";
				testErrorMessage = errMsg.str();
				this->signalStop();
			}
		}
	}
}

void TimerTestAO::onStop(void)
{
	if (None != testCase_)
	{
		size_t expectedCount = 0;
		switch (testCase_)
		{
		case OneShot:
			expectedCount = 5;
			break;
		case MultiOneShot:
			expectedCount = MultiOneShotTimerCount;
			break;
		case Repeat:
			expectedCount = 15;
			break;
		}
		if (timeoutCount_ != expectedCount)
		{
			std::stringstream errMsg;
			errMsg << "Received " << timeoutCount_ << " timeouts, expected " << expectedCount;
			testErrorMessage = errMsg.str();
		}
	}
}

void TimerTestAO::handle(SpiFoundation::RoomFW::RoomMessageSP message)
{
	//
	// TODO:  Handle message for each port.
	//
	switch (message->portIndex().port())
	{
	case PORT_TIMER: // Handle timer messages.
		{
			// Get diff time right away.
			int diff = this->diffTime();
			++timeoutCount_;
			//
			// Handle timer id.
			//
			SpiFoundation::RoomFW::RoomTimer::TimerId timerId = message->messageID();
			ASSERT(SpiFoundation::RoomFW::RoomTimer::InvalidTimerId != timerId);
			if (timerId != timerId_)
			{
				std::stringstream errMsg;
				errMsg << "Received invalid timer id=" << timerId << ", expected " << timerId_;
				testErrorMessage = errMsg.str();
				this->signalStop();
				return;
			}
			int diff2 = ::abs(diff - static_cast<int>(timeout_));
			if (diff2 > timerResolution_)
			{
				std::stringstream errMsg;
				errMsg << "Timeout=" << timeout_ << ", Actual=" << diff << ", Resolution=" << timerResolution_;
				testErrorMessage = errMsg.str();
				this->signalStop();
				return;
			}
			else
			{
				G_LOG_3(LV_SOFTWARE, "Timeout=%u, Actual=%i, Resolution=%u", timeout_, diff, timerResolution_);
			}
			if (MultiOneShot == testCase_)
			{
				if (timeoutCount_ >= MultiOneShotTimerCount)
				{
					timerIdList_.pop_front();
					while (!timerIdList_.empty())
					{
						if (!this->cancelTimer(timerIdList_.front()))
						{
							std::stringstream errMsg;
							errMsg << "MultiOneShot Cancel timer id=" << timerIdList_.front() << " failed";
							testErrorMessage = errMsg.str();
						}
						timerIdList_.pop_front();
					}
					this->signalStop();
					return;
				}
				else
				{
					timerIdList_.pop_front();
					timerId_ = timerIdList_.front();
					timeout_ += 20;
				}
			}
			else
			{
				if ((OneShot == testCase_) ||
					((Repeat == testCase_) && (++count_ >= 3)))
				{
					if (Repeat == testCase_)
					{
						if (!this->cancelTimer(timerId_))
						{
							std::stringstream errMsg;
							errMsg << "Cancel timer id=" << timerId_ << " failed";
							testErrorMessage = errMsg.str();
							this->signalStop();
						}
						count_ = 0;
					}
					switch (timeout_)
					{
					case 2000:
						this->signalStop();
						return;
					case 1000:
						timeout_ = 2000;
						break;
					default:
						timeout_ *= 10;
					}
					TimeValue tv;
					tv.setMilliseconds(timeout_);
					timerId_ = this->setTimer(tv, ((OneShot == testCase_) ? 0 : tv));
					if (RoomTimer::InvalidTimerId == timerId_)
					{
						std::stringstream errMsg;
						errMsg << "Set timer failed with timeout of " << timeout_ << " ms";
						testErrorMessage = errMsg.str();
						this->signalStop();
					}
				}
				::_ftime(&startTime_);
			}
		}
		break;
	default:
		ASSERT(FALSE); // No more ports!
	}
}

int TimerTestAO::diffTime()
{
	_timeb endTime;
	::_ftime(&endTime);
	return (((endTime.time - startTime_.time) * 1000) +
			(static_cast<int>(endTime.millitm) - static_cast<int>(startTime_.millitm)));
}

static const DWORD WaitMS = 2000;

TimerTestSuite::TimerTestSuite(void) :
	testObj_()
{
	static const size_t repeat = 1;
	for (size_t i = 0; i < repeat; ++i)
	{
		LOG_TEST_ADD(TimerTestSuite::empty)
		LOG_TEST_ADD(TimerTestSuite::oneShot)
		LOG_TEST_ADD(TimerTestSuite::multiOneShot)
		LOG_TEST_ADD(TimerTestSuite::repeat)
		LOG_TEST_ADD(TimerTestSuite::starvation)
		LOG_TEST_ADD(TimerTestSuite::RobotTimeout)
		LOG_TEST_ADD(TimerTestSuite::MessageFlood)
	}
}

TimerTestSuite::~TimerTestSuite()
{
}

void TimerTestSuite::setup()
{
	LoggedTestSuite::setup();
}

void TimerTestSuite::tear_down()
{
	LoggedTestSuite::tear_down();
}

void TimerTestSuite::empty(void)
{
	TEST_ASSERT(testObj_.start(WaitMS));
	TEST_ASSERT(testObj_.isRunning(WaitMS));
	TEST_ASSERT(testObj_.stop(WaitMS));
	TEST_ASSERT_MSG(!testObj_.isErrored(), testObj_.errorMessage().c_str());
	TEST_ASSERT_MSG(testObj_.testErrorMessage.empty(), testObj_.testErrorMessage.c_str());
}

void TimerTestSuite::oneShot(void)
{
	testObj_.setTestCase(TimerTestAO::OneShot);
	TEST_ASSERT(testObj_.start(WaitMS));
	TEST_ASSERT(testObj_.isRunning(WaitMS));
	TEST_ASSERT(testObj_.wait(4000));
	TEST_ASSERT_MSG(!testObj_.isErrored(), testObj_.errorMessage().c_str());
	TEST_ASSERT_MSG(testObj_.testErrorMessage.empty(), testObj_.testErrorMessage.c_str());
}

void TimerTestSuite::multiOneShot(void)
{
	try
	{
		TimerTestAO::Ptr testObj(new TimerTestAO());
		testObj->setTestCase(TimerTestAO::MultiOneShot);
		TEST_ASSERT(testObj->start(WaitMS));
		TEST_ASSERT(testObj->isRunning(WaitMS));
		::Sleep(WaitMS);
		TEST_ASSERT(testObj->stop(WaitMS));
		TEST_ASSERT_MSG(!testObj->isErrored(), testObj->errorMessage().c_str());
		TEST_ASSERT_MSG(testObj->testErrorMessage.empty(), testObj->testErrorMessage.c_str());
	}
	catch(std::runtime_error& e)
	{
		TEST_ASSERT_MSG(false, e.what());
	}
	catch(...)
	{
		TEST_ASSERT_MSG(false, "Unhandled Exception");
	}
}

void TimerTestSuite::repeat(void)
{
	testObj_.setTestCase(TimerTestAO::Repeat);
	TEST_ASSERT(testObj_.start(WaitMS));
	TEST_ASSERT(testObj_.isRunning(WaitMS));
	TEST_ASSERT(testObj_.wait(12000));
	TEST_ASSERT_MSG(!testObj_.isErrored(), testObj_.errorMessage().c_str());
	TEST_ASSERT_MSG(testObj_.testErrorMessage.empty(), testObj_.testErrorMessage.c_str());
}

using namespace SpiControl::Protocol;
using namespace SpiFoundation::ErrorHandling;

class StarvationHandler : public ActiveObject::MessageHandler
{
public:
	StarvationHandler(ActiveObject* parent) :
	  ActiveObject::MessageHandler(parent),
	  timeoutCount_(0)
	{}

	virtual bool handle(RoomMessageSP message)
	{
		switch (message->portIndex().port())
		{
		case TestActiveInterface::PORT_MY_PORT_1:
			switch (message->messageID())
			{
			case GenericTestProtocol::IN_INT_MESSAGE1:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					TimeValue timeout;
					timeout.setMilliseconds(p->value());
					this->setTimer(timeout, timeout);
				}
				break;
			case GenericTestProtocol::IN_INT_MESSAGE2:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					messageCount_ = p->value();
				}
				break;
			case GenericTestProtocol::IN_INT_MESSAGE3:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					::Sleep(p->value());
					if (!--messageCount_)
					{
						throw std::exception("Test Complete"); // Exit thread.
					}
				}
				break;
			default:
				ASSERT(false);
			}
			break;
		case TestActiveInterface::PORT_TIMER:
			++timeoutCount_;
			break;
		default:
			ASSERT(false);
		}
		return true;
	}

	typedef boost::shared_ptr<StarvationHandler> Ptr;

	long timeoutCount_;
	long messageCount_;
};

void TimerTestSuite::starvation(void)
{
	//
	// Create test object, portal, binding, start.
	//
	TestActiveObjectSP tao(new TestActiveObject("tao"));
	StarvationHandler::Ptr mh(new StarvationHandler(tao.get()));
	tao->mapMessageHandler(PortIndex::INVALID, mh);
	EventPortalSP ep(new EventPortal("ep"));
	TEST_ASSERT(ep->initialize());
	PortalBinding binding(ep, tao, TestActiveInterface::PORT_MY_PORT_1);
	TEST_ASSERT(tao->start(DWait));
	try
	{

		// Send the first message to set up 100ms timeouts.
		static const int timeoutInterval = 100;
		ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE1, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(timeoutInterval))));

		// Send messages to keep it busy sleeping for 2 seconds.
		static const int timeoutCount = DWait / timeoutInterval;
		static const int sleepFactor = 4;
		static const int sleepCount = timeoutCount * sleepFactor;
		static const int sleepInterval = timeoutInterval / sleepFactor;
		ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE2, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(sleepCount))));
		for (int i = 0; i < sleepCount; ++i)
		{
			ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE3, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(sleepInterval))));
		}
		// Wait up to 2 seconds for test plus standard wait for overhead.
		TEST_ASSERT(tao->wait(2 * DWait));
		std::stringstream msg;
		msg << "Expected timeouts: " << timeoutCount << ", Actual timeouts: " << mh->timeoutCount_;
		TEST_ASSERT_DELTA_MSG(mh->timeoutCount_, timeoutCount, sleepFactor, msg.str().c_str());
	}
	catch (SendMessageError& e) // Messaging error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, e.what());
		if (e.getReason() == SendMessageError::SMER_RECEIVER_ERRORED)
		{
			TEST_ASSERT_MSG(!tao->isErrored(), tao->errorMessage().c_str());
		}
		else
		{
			TEST_ASSERT_MSG(false, e.what());
		}
	}
	catch (std::runtime_error& e) // Binding error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, e.what());
		TEST_ASSERT_MSG(false, e.what());
	}
	catch (...) // What the heck error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, "Unhandled Exception");
		TEST_ASSERT_MSG(false, "Unhandled Exception");
	}
}

class RobotTimeoutHandler : public ActiveObject::MessageHandler
{
public:
	RobotTimeoutHandler(ActiveObject* parent) :
	  ActiveObject::MessageHandler(parent),
	  timeoutCount_(0),
	  timerId_(RoomTimer::InvalidTimerId)
	{}

	virtual bool handle(RoomMessageSP message)
	{
		switch (message->portIndex().port())
		{
		case TestActiveInterface::PORT_MY_PORT_1:
			switch (message->messageID())
			{
			case GenericTestProtocol::IN_INT_MESSAGE1:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					TimeValue timeout;
					timeout.setMilliseconds(p->value());
					timerId_ = this->setTimer(timeout);
					if (RoomTimer::InvalidTimerId == timerId_)
					{
						throw std::runtime_error("Invalid TimerId");
					}
				}
				break;
			case GenericTestProtocol::IN_VOID_MESSAGE1:
				{
					if (!this->cancelTimer(timerId_))
					{
						throw std::runtime_error("Cancel Timer failed");
					}
				}
				break;
			case GenericTestProtocol::IN_VOID_MESSAGE2:
				{
					if (timeoutCount_)
					{
						throw std::exception("Test Failed"); // Exit thread.
					}
					else
					{
						throw std::exception("Test Complete"); // Exit thread.
					}
				}
				break;
			default:
				ASSERT(false);
			}
			break;
		case TestActiveInterface::PORT_TIMER:
			++timeoutCount_;
			break;
		default:
			ASSERT(false);
		}
		return true;
	}

	typedef boost::shared_ptr<RobotTimeoutHandler> Ptr;

	long timeoutCount_;
	RoomTimer::TimerId timerId_;
};

void TimerTestSuite::RobotTimeout(void)
{
	//
	// Create test object, portal, binding, start.
	//
	TestActiveObjectSP tao(new TestActiveObject("tao"));
	RobotTimeoutHandler::Ptr mh(new RobotTimeoutHandler(tao.get()));
	tao->mapMessageHandler(PortIndex::INVALID, mh);
	EventPortalSP ep(new EventPortal("ep"));
	TEST_ASSERT(ep->initialize());
	PortalBinding binding(ep, tao, TestActiveInterface::PORT_MY_PORT_1);
	TEST_ASSERT(tao->start(DWait));
	try
	{
		static const int messageCount = 1800;
		for (int i = 0; i < messageCount; ++i)
		{
			ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE1, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(500))));
			ep->sendMessage(GenericTestProtocol::IN_VOID_MESSAGE1);
		}
		ep->sendMessage(GenericTestProtocol::IN_VOID_MESSAGE2);
		TEST_ASSERT(tao->wait());
		TEST_ASSERT_MSG((tao->errorMessage() == "tao: Test Complete"), tao->errorMessage().c_str());
		std::stringstream threadStr;
#if ACTIVE_OBJECT_TIMER_THREADS
		threadStr << "tao->timerThreads_.size() = " << tao->timerThreads_.size();
		TEST_ASSERT(!mh->timeoutCount_);
		TEST_ASSERT_MSG(tao->timerThreads_.empty(), threadStr.str().c_str());
#endif
	}
	catch (SendMessageError& e) // Messaging error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, e.what());
		if (e.getReason() == SendMessageError::SMER_RECEIVER_ERRORED)
		{
			TEST_ASSERT_MSG(!tao->isErrored(), tao->errorMessage().c_str());
		}
		else
		{
			TEST_FAIL(e.what());
		}
	}
	catch (std::runtime_error& e) // Binding error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, e.what());
		TEST_ASSERT_MSG(false, e.what());
	}
	catch (...) // What the heck error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, "Unhandled Exception");
		TEST_ASSERT_MSG(false, "Unhandled Exception");
	}
}

class MessageFloodHandler : public ActiveObject::MessageHandler
{
public:
	MessageFloodHandler(ActiveObject* parent) :
	  ActiveObject::MessageHandler(parent),
	  timeoutCount_(0),
	  messageCount_(0)
	{}

	virtual bool handle(RoomMessageSP message)
	{
		switch (message->portIndex().port())
		{
		case TestActiveInterface::PORT_MY_PORT_1:
			switch (message->messageID())
			{
			case GenericTestProtocol::IN_INT_MESSAGE1:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					messageCount_ = p->value();
				}
				break;
			case GenericTestProtocol::IN_INT_MESSAGE2:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					TimeValue timeout;
					timeout.setMilliseconds(p->value());
					this->setTimer(timeout);
				}
				break;
			case GenericTestProtocol::IN_INT_MESSAGE3:
				{
					IntPayloadSP p = boost::dynamic_pointer_cast<IntPayload, IPayload>(message->releasePayload());
					ASSERT(p);
					::Sleep(p->value());
					if (!--messageCount_)
					{
						throw std::exception("Test Complete"); // Exit thread.
					}
				}
				break;
			default:
				ASSERT(false);
			}
			break;
		case TestActiveInterface::PORT_TIMER:
			++timeoutCount_;
			break;
		default:
			ASSERT(false);
		}
		return true;
	}

	typedef boost::shared_ptr<MessageFloodHandler> Ptr;

	long timeoutCount_;
	long messageCount_;
};

void TimerTestSuite::MessageFlood(void)
{
	//
	// Create test object, portal, binding, start.
	//
	TestActiveObjectSP tao(new TestActiveObject("tao"));
	MessageFloodHandler::Ptr mh(new MessageFloodHandler(tao.get()));
	tao->mapMessageHandler(PortIndex::INVALID, mh);
	EventPortalSP ep(new EventPortal("ep"));
	TEST_ASSERT(ep->initialize());
	PortalBinding binding(ep, tao, TestActiveInterface::PORT_MY_PORT_1);
	TEST_ASSERT(tao->start(DWait));
	try
	{
		// Specify how many sleep messages to process.
		static const int queueLevel = 60;
		static const int sleepCount = 2 * queueLevel;
		static const int timeoutInterval = 20;
		static const int messageCount = sleepCount + 1; // Extra message to let last timer expire.
		static const int testTime = messageCount * timeoutInterval;
		static const int timeoutCount = queueLevel + sleepCount;
		ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE1, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(messageCount))));

		// Send timeouts to fill the queue.
		for (int i = 0; i < queueLevel; ++i)
		{
			ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE2, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(timeoutInterval * i))));
		}
		// Send additional timeouts and sleeps to keep the queues full.
		for (int i = 0; i < sleepCount; ++i)
		{
			ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE2, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(timeoutInterval))));
			ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE3, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(timeoutInterval))));
		}
		// Send additional sleep to expire timers.
		ep->sendMessage(GenericTestProtocol::IN_INT_MESSAGE3, IPayload::ReleaseOwnership(IntPayloadSP(new IntPayload(timeoutInterval))));
		long testWait = 0;
		TEST_ASSERT(tao->wait(testTime + DWait));
		std::stringstream msg;
		msg << "Expected timeouts: " << timeoutCount << ", Actual timeouts: " << mh->timeoutCount_;
		TEST_ASSERT_DELTA_MSG(mh->timeoutCount_, timeoutCount, 1, msg.str().c_str());
#if ACTIVE_OBJECT_TIMER_THREADS
		std::stringstream threadStr;
		threadStr << "tao->timerThreads_.size() = " << tao->timerThreads_.size();
		TEST_ASSERT_MSG(tao->timerThreads_.empty(), threadStr.str().c_str());
#endif
	}
	catch (SendMessageError& e) // Messaging error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, e.what());
		if (e.getReason() == SendMessageError::SMER_RECEIVER_ERRORED)
		{
			TEST_ASSERT_MSG(!tao->isErrored(), tao->errorMessage().c_str());
		}
		else
		{
			TEST_ASSERT_MSG(false, e.what());
		}
	}
	catch (std::runtime_error& e) // Binding error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, e.what());
		TEST_ASSERT_MSG(false, e.what());
	}
	catch (...) // What the heck error.
	{
		G_LOG_0(SpiFoundation::RoomFW::LV_ERROR, "Unhandled Exception");
		TEST_ASSERT_MSG(false, "Unhandled Exception");
	}
}

}}}
